﻿using Microsoft.Toolkit.Uwp.UI.Controls;
using PEAnalyzer.ModeView.PEInfoPage;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Foundation.Metadata;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;

// https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板

namespace PEAnalyzer
{
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class PEInfoPage : Page
    {
        private CoreInterface.PEInfo m_pPeInfo;

        private PEInfoModeView m_pModeView = new PEInfoModeView();

        private string m_strLastSortImportFunctionColumsTag = "";
        public PEInfoPage()
        {
            this.InitializeComponent();

            DataGrid.DataContext = m_pModeView;
        }

        private void ImportFunctionsDataGrid_Sorting(object sender, DataGridColumnEventArgs e)
        {
            DataGrid pDataGrid = sender as DataGrid;
            if(m_strLastSortImportFunctionColumsTag != e.Column.Tag.ToString())
            {
                foreach (var columnItem in pDataGrid.Columns)
                {
                    columnItem.SortDirection = null;
                }
            }
            m_strLastSortImportFunctionColumsTag = e.Column.Tag.ToString();

            var pImportModuleItem = ImportModuleList.SelectedItem as ImportModuleItem;
            if(pImportModuleItem != null)
            {
                if (e.Column.SortDirection == null)
                {

                    pImportModuleItem.ImportFunctions = ImportFunctionSort(pImportModuleItem.ImportFunctions, e.Column.Tag.ToString(), true);
                    e.Column.SortDirection = DataGridSortDirection.Ascending;
                }
                else if (e.Column.SortDirection == DataGridSortDirection.Ascending)
                {
                    pImportModuleItem.ImportFunctions = ImportFunctionSort(pImportModuleItem.ImportFunctions, e.Column.Tag.ToString(), false);
                    e.Column.SortDirection = DataGridSortDirection.Descending;
                }
                else
                {
                    e.Column.SortDirection = null;
                }
            }
        }

        private ObservableCollection<ImportFunctionItem> ImportFunctionSort(ObservableCollection<ImportFunctionItem> data, string tag, bool ascending)
        {
            ObservableCollection<ImportFunctionItem> ret = null;
            switch (tag)
            {
                case "SerialNumber":
                    {
                        if(ascending)
                        {
                            ret = new ObservableCollection<ImportFunctionItem>(from item in data orderby item.SerialNumber ascending select item);
                        }
                        else
                        {
                            ret = new ObservableCollection<ImportFunctionItem>(from item in data orderby item.SerialNumber descending select item);
                        }
                    }
                    break;

                case "FunctionName":
                    {
                        if (ascending)
                        {
                            ret = new ObservableCollection<ImportFunctionItem>(from item in data orderby item.FunctionName ascending select item);
                        }
                        else
                        {
                            ret = new ObservableCollection<ImportFunctionItem>(from item in data orderby item.FunctionName descending select item);
                        }
                    }
                    break;

                case "ModuleName":
                    {
                        if (ascending)
                        {
                            ret = new ObservableCollection<ImportFunctionItem>(from item in data orderby item.ModuleName ascending select item);
                        }
                        else
                        {
                            ret = new ObservableCollection<ImportFunctionItem>(from item in data orderby item.ModuleName descending select item);
                        }
                    }
                    break;
                default:
                    ret = new ObservableCollection<ImportFunctionItem>();
                    break;
            }

            return ret;
        }

        private void ExportFunctionsDataGrid_Sorting(object sender, DataGridColumnEventArgs e)
        {
            DataGrid pDataGrid = sender as DataGrid;
            if (m_strLastSortImportFunctionColumsTag != e.Column.Tag.ToString())
            {
                foreach (var columnItem in pDataGrid.Columns)
                {
                    columnItem.SortDirection = null;
                }
            }
            m_strLastSortImportFunctionColumsTag = e.Column.Tag.ToString();

            if (e.Column.SortDirection == null)
            {
                m_pModeView.ExportFunctions = ExportFunctionSort(m_pModeView.ExportFunctions, e.Column.Tag.ToString(), true);
                e.Column.SortDirection = DataGridSortDirection.Ascending;
            }
            else if (e.Column.SortDirection == DataGridSortDirection.Ascending)
            {
                m_pModeView.ExportFunctions = ExportFunctionSort(m_pModeView.ExportFunctions, e.Column.Tag.ToString(), false);
                e.Column.SortDirection = DataGridSortDirection.Descending;
            }
            else
            {
                e.Column.SortDirection = null;
            }
        }

        private ObservableCollection<ExportFunctionModeView> ExportFunctionSort(ObservableCollection<ExportFunctionModeView> data, string tag, bool ascending)
        {
            ObservableCollection<ExportFunctionModeView> ret = null;
            switch (tag)
            {
                case "Name":
                    {
                        if (ascending)
                        {
                            ret = new ObservableCollection<ExportFunctionModeView>(from item in data orderby item.Name ascending select item);
                        }
                        else
                        {
                            ret = new ObservableCollection<ExportFunctionModeView>(from item in data orderby item.Name descending select item);
                        }
                    }
                    break;

                case "SerialNumber":
                    {
                        if (ascending)
                        {
                            ret = new ObservableCollection<ExportFunctionModeView>(from item in data orderby item.SerialNumber ascending select item);
                        }
                        else
                        {
                            ret = new ObservableCollection<ExportFunctionModeView>(from item in data orderby item.SerialNumber descending select item);
                        }
                    }
                    break;

                case "Address":
                    {
                        if (ascending)
                        {
                            ret = new ObservableCollection<ExportFunctionModeView>(from item in data orderby item.Address ascending select item);
                        }
                        else
                        {
                            ret = new ObservableCollection<ExportFunctionModeView>(from item in data orderby item.Address descending select item);
                        }
                    }
                    break;
                default:
                    ret = new ObservableCollection<ExportFunctionModeView>();
                    break;
            }

            return ret;
        }

        private void BackInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
        {
            On_BackRequested();
            args.Handled = true;
        }

        private bool On_BackRequested()
        {
            if (this.Frame.CanGoBack)
            {
                this.Frame.GoBack();
                return true;
            }
            return false;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            BackButton.IsEnabled = this.Frame.CanGoBack;

            PageParameter pageParameter = e.Parameter as PageParameter;
            if (pageParameter != null)
            {
                InitlizeModeViewData(pageParameter);
            }

            base.OnNavigatedTo(e);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            On_BackRequested();
        }

        private void Page_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            var point = e.GetCurrentPoint(sender as Page);
            if(point.Properties.IsXButton1Pressed == true)
            {
                //后退键
                On_BackRequested();
            }
        }

        private void InitlizeModeViewData(PageParameter parameter)
        {
            m_pPeInfo = parameter.PEInfo;
            m_pModeView.Info = parameter.Info;
            m_pModeView.FileName = m_pPeInfo.Get_FileName();
            m_pModeView.FullPath = m_pPeInfo.Get_FullPath();

            var pDosHeader = m_pPeInfo.Get_ImageDosHeader();
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_magic", pDosHeader.Get_magic(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_cblp", pDosHeader.Get_cblp(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_cp", pDosHeader.Get_cp(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_crlc", pDosHeader.Get_crlc(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_cparhdr", pDosHeader.Get_cparhdr(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_minalloc", pDosHeader.Get_minalloc(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_maxalloc", pDosHeader.Get_maxalloc(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_ss", pDosHeader.Get_ss(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_sp", pDosHeader.Get_sp(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_csum", pDosHeader.Get_csum(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_ip", pDosHeader.Get_ip(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_cs", pDosHeader.Get_cs(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_lfarlc", pDosHeader.Get_lfarlc(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_ovno", pDosHeader.Get_ovno(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_oemid", pDosHeader.Get_oemid(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_oeminfo", pDosHeader.Get_oeminfo(), "IMAGE_DOS_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("e_lfanew", pDosHeader.Get_lfanew(), "IMAGE_DOS_HEADER"));

            var pNtHeaders = m_pPeInfo.Get_ImageNtHeader();
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("Signature", pNtHeaders.Get_Signature(), "IMAGE_NT_HEADERS"));

            var pFileHeader = pNtHeaders.Get_FileHeader();
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("Machine", pFileHeader.Get_Machine(), "IMAGE_FILE_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("NumberOfSections", pFileHeader.Get_NumberOfSections(), "IMAGE_FILE_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("TimeDateStamp", pFileHeader.Get_TimeDateStamp(), "IMAGE_FILE_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("NumberOfSymbols", pFileHeader.Get_NumberOfSymbols(), "IMAGE_FILE_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfOptionalHeader", pFileHeader.Get_SizeOfOptionalHeader(), "IMAGE_FILE_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("Characteristics", pFileHeader.Get_Characteristics(), "IMAGE_FILE_HEADER"));

            var pOptionalHeader = pNtHeaders.Get_OptionalHeader();
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("Magic", pOptionalHeader.Get_Magic(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("MajorLinkerVersion", pOptionalHeader.Get_MajorLinkerVersion(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("MinorLinkerVersion", pOptionalHeader.Get_MinorLinkerVersion(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfCode", pOptionalHeader.Get_SizeOfCode(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfInitializedData", pOptionalHeader.Get_SizeOfInitializedData(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfUninitializedData", pOptionalHeader.Get_SizeOfUninitializedData(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("AddressOfEntryPoint", pOptionalHeader.Get_AddressOfEntryPoint(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("BaseOfCode", pOptionalHeader.Get_BaseOfCode(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("BaseOfData", pOptionalHeader.Get_BaseOfData(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("ImageBase", pOptionalHeader.Get_ImageBase(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SectionAlignment", pOptionalHeader.Get_SectionAlignment(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("FileAlignment", pOptionalHeader.Get_FileAlignment(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("MajorOperatingSystemVersion", pOptionalHeader.Get_MajorOperatingSystemVersion(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("MinorOperatingSystemVersion", pOptionalHeader.Get_MinorOperatingSystemVersion(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("MajorImageVersion", pOptionalHeader.Get_MajorImageVersion(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("MinorImageVersion", pOptionalHeader.Get_MinorImageVersion(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("MajorSubsystemVersion", pOptionalHeader.Get_MajorSubsystemVersion(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("MinorSubsystemVersion", pOptionalHeader.Get_MinorSubsystemVersion(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("Win32VersionValue", pOptionalHeader.Get_Win32VersionValue(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfImage", pOptionalHeader.Get_SizeOfImage(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfHeaders", pOptionalHeader.Get_SizeOfHeaders(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("CheckSum", pOptionalHeader.Get_CheckSum(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("Subsystem", pOptionalHeader.Get_Subsystem(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("DllCharacteristics", pOptionalHeader.Get_DllCharacteristics(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfStackReserve", pOptionalHeader.Get_SizeOfStackReserve(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfStackCommit", pOptionalHeader.Get_SizeOfStackCommit(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfHeapReserve", pOptionalHeader.Get_SizeOfHeapReserve(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("SizeOfHeapCommit", pOptionalHeader.Get_SizeOfHeapCommit(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("LoaderFlags", pOptionalHeader.Get_LoaderFlags(), "IMAGE_OPTIONAL_HEADER"));
            m_pModeView.HeaderValues.Add(new HeaderValueModeView("NumberOfRvaAndSizes", pOptionalHeader.Get_NumberOfRvaAndSizes(), "IMAGE_OPTIONAL_HEADER"));

            foreach (var sectionHeader in m_pPeInfo.Get_SectionHeaders())
            {
                SectionHeaderViewItem item = new SectionHeaderViewItem();
                item.Name = "";
                foreach (byte ch in sectionHeader.Get_Name())
                {
                    item.Name += System.Convert.ToChar(ch);
                }

                item.VirtualAddress = sectionHeader.Get_VirtualAddress();
                item.VirtualSize = sectionHeader.Get_VirtualSize();
                item.SizeOfRawData = sectionHeader.Get_SizeOfRawData();
                item.PointerToRawData = sectionHeader.Get_PointerToRawData();
                item.PointerToLinenumbers = sectionHeader.Get_PointerToLinenumbers();
                item.NumberOfRelocations = sectionHeader.Get_NumberOfRelocations();
                item.NumberOfLinenumbers = sectionHeader.Get_NumberOfLinenumbers();
                item.Characteristics = sectionHeader.Get_Characteristics();

                m_pModeView.SectionHeaderItems.Add(item);
            }

            //DataDirectory
            var pDataDirectory = m_pPeInfo.Get_ImageNtHeader().Get_OptionalHeader().Get_DataDirectory();

            m_pModeView.ExportTableAddress = pDataDirectory[0].Get_VirtualAddress();
            m_pModeView.ExportTableSize = pDataDirectory[0].Get_Size();

            m_pModeView.ImportTableAddress = pDataDirectory[1].Get_VirtualAddress();
            m_pModeView.ImportTableSize = pDataDirectory[1].Get_Size();

            m_pModeView.ResourceTableAddress = pDataDirectory[2].Get_VirtualAddress();
            m_pModeView.ResourceTableSize = pDataDirectory[2].Get_Size();

            m_pModeView.ExceptionTableAddress = pDataDirectory[3].Get_VirtualAddress();
            m_pModeView.ExceptionTableSize = pDataDirectory[3].Get_Size();

            m_pModeView.SecurityTableAddress = pDataDirectory[4].Get_VirtualAddress();
            m_pModeView.SecurityTableSize = pDataDirectory[4].Get_Size();

            m_pModeView.BaseRelocationTableAddress = pDataDirectory[5].Get_VirtualAddress();
            m_pModeView.BaseRelocationTableSize = pDataDirectory[5].Get_Size();

            m_pModeView.DebugTableAddress = pDataDirectory[6].Get_VirtualAddress();
            m_pModeView.DebugTableSize = pDataDirectory[6].Get_Size();

            m_pModeView.CopyrightTableAddress = pDataDirectory[7].Get_VirtualAddress();
            m_pModeView.CopyrightTableSize = pDataDirectory[7].Get_Size();

            m_pModeView.GlobalPtrTableAddress = pDataDirectory[8].Get_VirtualAddress();
            m_pModeView.GlobalPtrTableSize = pDataDirectory[8].Get_Size();

            m_pModeView.ThreadLocalStorageTableAddress = pDataDirectory[9].Get_VirtualAddress();
            m_pModeView.ThreadLocalStorageTableSize = pDataDirectory[9].Get_Size();

            m_pModeView.LoadConfigurationTableAddress = pDataDirectory[10].Get_VirtualAddress();
            m_pModeView.LoadConfigurationTableSize = pDataDirectory[10].Get_Size();

            m_pModeView.BoundImportTableAddress = pDataDirectory[11].Get_VirtualAddress();
            m_pModeView.BoundImportTableSize = pDataDirectory[11].Get_Size();

            m_pModeView.ImportAddressTableAddress = pDataDirectory[12].Get_VirtualAddress();
            m_pModeView.ImportAddressTableSize = pDataDirectory[12].Get_Size();

            m_pModeView.DelayImportTableAddress = pDataDirectory[13].Get_VirtualAddress();
            m_pModeView.DelayImportTableSize = pDataDirectory[13].Get_Size();

            m_pModeView.COMDecriptorTableAddress = pDataDirectory[14].Get_VirtualAddress();
            m_pModeView.COMDecriptorTableSize = pDataDirectory[14].Get_Size();

            //导出表
            var exportFunction = m_pPeInfo.Get_ExportFunction();
            if(exportFunction.Get_FunctionArray() != null)
            {
                foreach (var function in exportFunction.Get_FunctionArray())
                {
                    ExportFunctionModeView item = new ExportFunctionModeView();
                    item.Address = function.Get_Address();
                    item.Name = function.Get_Name();
                    item.SerialNumber = function.Get_SerialNumber();

                    m_pModeView.ExportFunctions.Add(item);
                }
            }
            

            //导入表
            var importModuleList = m_pPeInfo.Get_ImportFunctionArray();
            if(importModuleList != null)
            {
                foreach (var importTableItem in importModuleList)
                {
                    ImportModuleItem item = new ImportModuleItem();
                    item.ModuleName = importTableItem.Get_Name();
                    foreach (var importFunctionItem in importTableItem.Get_FuncArray())
                    {
                        item.ImportFunctions.Add(new ImportFunctionItem(importFunctionItem.Get_SerialNumber(), importFunctionItem.Get_Name(), importFunctionItem.Get_Address(), item.ModuleName));
                    }
                    // item.ImportFunctions = m_pModeView.ImportFunctions;
                    m_pModeView.ImportModules.Add(item);
                }
            }  
        }

        private void GridView_ItemClick(object sender, ItemClickEventArgs e)
        {
            SectionHeaderViewItem item = e.ClickedItem as SectionHeaderViewItem;
            ConnectedAnimation animation = null;

            do
            {
                if(item == null)
                {
                    break;
                }

                animation = SectionHeaderGridView.PrepareConnectedAnimation("forwardAnimation", item, "SectionHeaderDataTemplateElement");

                SectionHeaderSmokeGrid.Visibility = Visibility.Visible;
                animation.TryStart(SectionHeaderDesctionGrid);
            } while (false);
        }

        private async void SectionHeaderSmokeClose(object sender, RoutedEventArgs e)
        {
            ConnectedAnimation animation = ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("backwardsAnimation", SectionHeaderDesctionGrid);

            animation.Completed += SectionHeaderDesctionAnimation_Completed;

            if(ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7))
            {
                animation.Configuration = new DirectConnectedAnimationConfiguration();
            }

            await SectionHeaderGridView.TryStartConnectedAnimationAsync(animation, SectionHeaderGridView.SelectedItem, "SectionHeaderDataTemplateElement");
        }

        private void SectionHeaderDesctionAnimation_Completed(ConnectedAnimation sender, object args)
        {
            SectionHeaderSmokeGrid.Visibility = Visibility.Collapsed;
        }
    }
}
